<?php
	class DatabaseReverse extends Base {
		
		private $config;
		private $dataSource;
		private $connection;		
		private $reverse;
		private $autoexec;
		
		public function __construct($config) {
			$this->config = $config;
			$this->reverse = false;
			$this->autoexec = false;
			
			if( isset( $this->config["reverse"] ) ) $this->reverse = $this->config["reverse"]; 
			if( isset( $this->reverse["autoexec"] ) ) $this->autoexec = $this->reverse["autoexec"]; 
			
			if( $this->reverse && $this->autoexec ) {
				$this->dataSource = new DataSource($config["database"]);
				$this->connection = $this->dataSource->getPdo();
				$this->generate();
			}
		}
		
		private function generate() {
			if( !$this->reverse || !$this->autoexec ) return;
			
			$tables = $this->loadMetadata();
			$tables = $this->verifyAssociations($tables);
			$entities = $this->formatNames($tables);
			$this->buildModel($entities);
			//$this->buildActions($entities);
			$this->buildServices($entities);
			$this->buildDaos($entities);
		}			
		
		private function verifyAssociations($temp) {
			$tables = array();
						
			foreach( $temp as $table ) {
				$columns = $table["columns"];
				
				foreach( $columns as $column ) {
					foreach( $temp as $tempTable ) {
						$tempColumns = 	$tempTable["columns"];
							
						if( $table["name"] != $tempTable["name"] ) { // another table
								
							foreach( $tempColumns as $tempColumn ) {
									
								if( $column["name"] == $tempColumn["name"] || strpos($column["name"], $tempColumn["name"]) > -1 ) { // same column
									$origPK = $column["primary-key"];
									$origFK = $column["foreign-key"];
									$destPK = $tempColumn["primary-key"];
									$destFK = $tempColumn["foreign-key"];
									$origPKs = $this->getPKs($table);
									$destPKs = $this->getPKs($tempTable);									
									
									if( $origPK && $destPK && $destFK ) {
										//echo "01 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
									} else if( $origPK && !$destPK && $destFK ) {
										//echo "02 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
										$association = array();
										$association["hasMany"] = $tempTable["name"];
										$association["condicao"] = "orig pk != dest pk == dest fk";
										$column["association"] = $association;
									} else if( $origPK && $destPK && !$destFK ) {
										//echo "03 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
										$association = array();
										
										$count = 0;
										foreach( $origPKs as $origCols ) {
											foreach( $destPKs as $destCols ) {
												if( $origCols["name"] == $destCols["name"] || strpos($column["name"], $tempColumn["name"]) > -1 )
													$count++;
											}
										}
										
										$association["condicao"] = "orig pk == dest pk != dest fk";
										
										if( sizeof($origPKs) == $count ) { // tem todas as colunas da pk
											$association["hasMany"] = $tempTable["name"];											
										} else {
											$association["hasOne"] = $tempTable["name"];
										}
										
										$column["association"] = $association;
									} else if( $origFK && !$destPK && $destFK ) {
										//echo "04 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
									} else if( $origFK && $destPK && !$destFK ) {
										//echo "05 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
										$association = array();
										$association["hasOne"] = $tempTable["name"];
										$association["condicao"] = "orig pk == dest pk != dest fk";
										$column["association"] = $association;
									} else if( $origFK && $destPK && $destFK ) {
										//echo "06 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
									}
									
									/*if( $column["foreign-key"] && $tempColumn["primary-key"] ) {
										//echo "01 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
										$association = array();
										$association["hasOne"] = $tempTable["name"];
										$column["association"] = $association;
									} else if( $column["primary-key"] && $tempColumn["primary-key"] ) {
										//echo "03 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
										$association = array();
										$association["hasOne"] = $tempTable["name"];
										$column["association"] = $association;
									} else if( $column["primary-key"] && $tempColumn["foreign-key"] ) {
										//echo "04 - relacionamento da tabela " . $table["name"] . "." . $column["name"] . " com " . $tempTable["name"] . "." . $tempColumn["name"] . "<br />";
										if( $tempColumn["primary-key"] ) {
											
										}									
										
										$association = array();
										$association["hasMany"] = $tempTable["name"];
										$column["association"] = $association;
									} */
								}									
							}
						}							
					}
					$table[] = $column;					
				}
				$tables[] = $table;
			}
			
			//print_r($tables);
			
			return $tables;
		}
		
		private function getPKs($table) {
			$pks = array();
			
			$columns = $table["columns"];
			foreach( $columns as $column ) {
				if( $column["primary-key"] ) $pks[] = $column;				
			}
			
			return $pks;
		}
		
		private function buildActions($entities) {
			$package = isset($this->reverse["action-package"]) ? realpath(".") . "/" . $this->reverse["action-package"] : realpath(".") . "/classes/action";
			$methods = FileUtils::readFile(realpath(".") . "/quasenadafw/components/reverse/action-methods.template");
			
			FileUtils::createDirectory($package);
			
			$countEntities = 0;	
			
			while( $countEntities < sizeof($entities) ) {
				$countFields = 0;
				$entity = $entities[$countEntities];
				$fields = $entity["columns"];
				
				$fileName = $entity["name"] . "Action.php";
				$entityName = $entity["name"];
				
				// begin and constructor
				$content = "<?php\n\tclass " . $entityName . "Action extends BaseAction {\n\n";
				$content .= "\t\tpublic function __construct() {\n";
				$content .= "\t\t\tparent::__construct(true);\n";
				$content .= "\t\t}\n\n";
				
				$content .= $methods; 				

				$content .= "\n";
				
				$content .="\t}\n?>";
				
				$countEntities++;

				FileUtils::createFile($package, $fileName, $content);
			}
		}
		
		private function buildServices($entities) {
			$package = isset($this->reverse["service-package"]) ? realpath(".") . "/" . $this->reverse["service-package"] : realpath(".") . "/classes/service";
			
			FileUtils::createDirectory($package);
			
			$countEntities = 0;	
			
			while( $countEntities < sizeof($entities) ) {
				$countFields = 0;
				$entity = $entities[$countEntities];
				$fields = $entity["columns"];
				
				$fileName = $entity["name"] . "Service.php";
				$entityName = $entity["name"];
				
				// begin and constructor
				$content = "<?php\n\tclass " . $entityName . "Service extends BaseService {\n\n";
				$content .= "\t\tpublic function __construct() {\n";
				$content .= "\t\t\tparent::__construct();\n";
				$content .= "\t\t\t\$this->dao = new " . $entityName . "Dao(Config::getDateSourceName(), Config::\$db_user, Config::\$db_password);\n";
				$content .= "\t\t}\n";
				$content .= "\t}\n";
				$content .= "?>";
				
				$countEntities++;

				FileUtils::createFile($package, $fileName, $content);
			}
		}
		
		private function buildDaos($entities) {
			$package = isset($this->reverse["dao-package"]) ? realpath(".") . "/" . $this->reverse["dao-package"] : realpath(".") . "/classes/dao";
			
			FileUtils::createDirectory($package);
			
			$countEntities = 0;	
			
			while( $countEntities < sizeof($entities) ) {
				$countColumns = 0;
				$entity = $entities[$countEntities];
				$fields = $entity["columns"];
				
				$fileName = $entity["name"] . "Dao.php";
				$primaryKeys = "array(";
				$fieldsName = "array(";
				$columnsName = "array(";
				
				$firstKey = true;
				$firstField = true;
				$firstColumn = true;
				
				while( $countColumns < sizeof($fields) ) {
					$field = $fields[$countColumns];
					if( $field["primary-key"] ) {
						if( $firstKey ) {
							$primaryKeys .= "\"" . $field["name"] . "\"";
							$firstKey = false;
						} else {
							$primaryKeys .= ",\"" . $field["name"] . "\"";
						}
					}
					
					if( $firstField ) {
						$fieldsName .= "\"" . $field["name"] . "\"";
						$firstField = false;
					} else {
						$fieldsName .= ",\"" . $field["name"] . "\"";
					}
					
					if( $firstColumn ) {
						$columnsName .= "\"" . $field["column"] . "\"";
						$firstColumn = false;
					} else {
						$columnsName .= ",\"" . $field["column"] . "\"";
					}					
					
					$countColumns++;
				}
				
				$primaryKeys .= ")";
				$fieldsName .= ")";
				$columnsName .= ")";
				
				
				// begin and constructor
				$content = "<?php\n\tclass " . $entity["name"] . "Dao extends BaseDao {\n\n";
				$content .= "\t\tpublic function __construct(\$dns, \$username, \$password) {\n";
				$content .= "\t\t\tparent::__construct(\$dns, \$username, \$password);\n\n";
				$content .= "\t\t\t\$this->mapping = new Mapping(\"" . $entity["name"] . "\", \"" . $entity["table"] ."\", " . $primaryKeys . ",\n";
				$content .= "\t\t\t\t" . $fieldsName . ",\n";
				$content .= "\t\t\t\t" . $columnsName . "\n\t\t\t);\n";
				$content .= "\t\t}\n\n}";
								
				$countEntities++;

				FileUtils::createFile($package, $fileName, $content);
			}			
		}
		
		private function buildModel($entities) {
			$package = isset($this->reverse["model-package"]) ? realpath(".") . "/" . $this->reverse["model-package"] : realpath(".") . "/classes/model";
			
			FileUtils::createDirectory($package);
			
			$getset = FileUtils::readFile(realpath(".") . "/quasenadafw/components/reverse/getter-setter.template");
			
			$index = 0;
			
			while( $index < sizeof($entities) ) {
				$entity = $entities[$index];
				
				$content = "";
				$entityName = $entity["name"];
				$filename = $entity["name"] . ".php";
				
				$content .= "<?php\n\tclass $entityName extends BaseModel {\n\n"; 
				$fields = $entity["columns"];
				
				$metodos = "";
				$required = "\t\tpublic function required() {\n\t\t\treturn array(\n";
				$json = "\t\tpublic function json() {\n\t\t\treturn array(\n";
				
				$count = 0;
				
				while( $count < sizeof($fields) ) {
					$field = $fields[$count];
					$fieldName = $field["name"];					
					
					$content .= "\t\tprivate \$$fieldName;\n";
					
					$metodo = $getset;
					$metodo = StringUtils::replace("@field_upper@", StringUtils::ucfirst($fieldName), $metodo);
					$metodo = StringUtils::replace("@field_lower@", $fieldName, $metodo);
					$metodos .= $metodo;
					
					if( $field["not-null"] ) $required .= "\t\t\t\t\"$fieldName\" => \$this->$fieldName";
					
					$json .= "\t\t\t\t\"$fieldName\" => \$this->$fieldName";
					
					if( $count + 1 < sizeof($fields) ) {
						if( $field["not-null"] ) $required .= ",\n";
						$json .= ",\n";
					} else {
						if( $field["not-null"] ) $required .= "\n";
						$json .= "\n";
					}
					
					$count++;
				}
				
				$required .= "\t\t\t);\n\t\t}\n";
				$json .= "\t\t\t);\n\t\t}\n";
				
				$content .= "\n";
				
				$content .= $metodos;
				
				$content .= $required;
				
				$content .= "\n";
				
				$content .= $json;
				
				$content .= "\n";
				
				$content .= "\t}";
				
				$index++;
				
				FileUtils::createFile($package, $filename, $content);
			}
		}

		private function formatNames($temp) {
			$entities = array();
			
			$prefix = isset($this->reverse["prefix-table"]) ? $this->reverse["prefix-table"] : null;
			
			foreach( $temp as $entity ) {
				
				if( $prefix != null ) {
					$name = $entity["name"];
					$newName = StringUtils::replace($prefix, "", $name);
					$newName = StringUtils::toCamelCase($newName, true);
					$entity["name"] = $newName;
					$entity["table"] = $name;
				}
				
				$fields = array();
				$tempFields = $entity["columns"];
				
				foreach( $tempFields as $field ) {
					$name = $field["name"];
					$newName = StringUtils::toCamelCase($name);
					
					if( StringUtils::replace("id", "", $newName) != "" && StringUtils::replace("id", "", $newName) != $entity["name"] ) 
						$newName = StringUtils::lcfirst(StringUtils::replace("id", "", $newName));
										
					$field["name"] = $newName;
					$field["column"] = $name;
					$fields[] = $field;
				}
				
				$entity["columns"] = $fields;
				$entities[] = $entity;
			}
			return $entities;
		}
		
		private function loadMetadata() {
			$tables = array();
			$showTables = $this->connection->query("SHOW TABLES");

			while( $resTables = $showTables->fetch(PDO::FETCH_NUM) ) {
				$table = array("name" => $resTables[0]);

				$showColumns = $this->connection->query("SHOW COLUMNS FROM " . $table["name"]);
				 
				$columns = array();
				while( $resColumns = $showColumns->fetch(PDO::FETCH_NUM) ) {
					$column = array();
					$column["name"] = $resColumns[0];
					$column["type"] = $resColumns[1];
					$column["not-null"] = $resColumns[2] == "NO" ? true : false;
					$column["primary-key"] = $resColumns[3] == "PRI" ? true : false;
					$column["foreign-key"] = $resColumns[3] == "MUL" ? true : false;
					$column["association"] = null;
					
					$columns[] = $column;
				}
				
				$table["columns"] = $columns;
				$tables[] = $table;
			}
			
			return $tables;
		}
	}
?>